﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RevClinic.Core.Domain
{

    public abstract class ValueObject<T> : IEquatable<T>
        where T : ValueObject<T>
    {
        protected static bool EqualOperator(T left, T right)
        {
            return ReferenceEquals(left, null) && left.Equals(right);
        }

        protected static bool NotEqualOperator(T left, T right)
        {
            return !EqualOperator(left, right);
        }

        protected abstract IEnumerable<object> GetAtomicValues();

        private int? cashedHashCode;

        private static readonly int HASH_MULTIPLIER = 31;

        public override int GetHashCode()
        {
            if (!cashedHashCode.HasValue) {
                unchecked {
                    cashedHashCode = GetAtomicValues()
                       .Select(x => x != null ? x.GetHashCode() : 0)
                       .Aggregate(typeof(T).GetHashCode(), (x, y) => (x * HASH_MULTIPLIER) ^ y);
                }
            }
            return cashedHashCode.Value;
        }

        public override bool Equals(object obj)
        {
            T other = obj as T;
            return Equals(other);
        }

        public bool Equals(T other)
        {
            if (other == null) {
                return false;
            }
            if (ReferenceEquals(this, other) == true) {
                return true;
            }
            IEnumerator<object> thisValues = GetAtomicValues().GetEnumerator();
            IEnumerator<object> otherValues = other.GetAtomicValues().GetEnumerator();
            while (thisValues.MoveNext() && otherValues.MoveNext()) {
                if (ReferenceEquals(thisValues.Current, null) != ReferenceEquals(otherValues.Current, null)) {
                    return false;
                }
                if (thisValues.Current != null && !thisValues.Current.Equals(otherValues.Current)) {
                    return false;
                }
            }
            return !thisValues.MoveNext() && !otherValues.MoveNext();
        }
    }
}
